﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;

namespace NeroConnect.Network
{
	class UdpClient : IUdpClient
	{
		private System.Net.Sockets.UdpClient _client;

        public UdpClient(int port)
        {
            IPEndPoint ep = new IPEndPoint(IPAddress.Any, port);
            InitClient(ep);
        }

        public UdpClient(IPEndPoint endPoint)
        {
            InitClient(endPoint);
        }

        void InitClient(IPEndPoint endPoint)
        {
            if (endPoint == null)
                throw new ArgumentNullException("endpoint cannot be NULL");

            if (endPoint.AddressFamily == AddressFamily.InterNetwork)
            {
                try
                {
                    _client = new System.Net.Sockets.UdpClient();
                    _client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                    _client.Client.Bind(endPoint);
                }
                catch (Exception any)
                {
                    Trace.TraceError("failed to start the UPD client on the port: " + endPoint.Port +
                        " with the exception " + any.Message);

                    throw any;
                }
            }
            else
            {
                // use the upd client directly
                _client = new System.Net.Sockets.UdpClient(endPoint);
            }
        }

        public UdpClient(int port, System.Net.Sockets.AddressFamily addressFamily)
        {
            if (addressFamily == AddressFamily.InterNetwork)
            {
                IPEndPoint point = null;
                point = new IPEndPoint(IPAddress.Any, port);
                
                InitClient(point);
            }
            else
            {
                _client = new System.Net.Sockets.UdpClient(port, addressFamily);
            }
        }

		public void Close()
		{
			_client.Close();
		}

		public int Send(byte[] data, int bytes, IPEndPoint endPoint)
		{
			return _client.Send(data, bytes, endPoint);
		}

		public byte[] Receive(ref IPEndPoint remoteEndPoint)
		{
			return _client.Receive(ref remoteEndPoint);
		}

		public IAsyncResult BeginSend(byte[] data, int bytes, IPEndPoint endPoint, AsyncCallback sendCallback, object state)
		{
			return _client.BeginSend(data, bytes, endPoint, sendCallback, state);
		}

		public int EndSend(IAsyncResult asyncResult)
		{
			return _client.EndSend(asyncResult);
		}

		public IAsyncResult BeginReceive(AsyncCallback receiveCallback, object state)
		{
			return _client.BeginReceive(receiveCallback, state);
		}

		public byte[] EndReceive(IAsyncResult asyncResult, ref IPEndPoint endPoint)
		{
			return _client.EndReceive(asyncResult, ref endPoint);
		}

		public void JoinMulticastGroup(IPAddress multicastAddress)
		{
			_client.JoinMulticastGroup(multicastAddress);
		}

		public void DropMulticastGroup(IPAddress multicastAddress)
		{
			_client.DropMulticastGroup(multicastAddress);
		}

		public int Available
		{
			get { return _client.Available; }
		}

		public bool MulticastLoopback
		{
			get
			{
				return _client.MulticastLoopback;
			}
			set
			{
				_client.MulticastLoopback = value;
			}
		}

		public Socket Client
		{
			get { return _client.Client; }
		}
	}
}
